Skip to content

Commit 583b4e9

Browse files
authored
Merge pull request #1931 from GaloisInc/T1859-mir_struct_value
`mir_find_adt`, `mir_struct_value`, and friends
2 parents cca5d53 + 1097b6a commit 583b4e9

38 files changed

+1213
-236
lines changed

crucible-mir-comp/src/Mir/Compositional/Builder.hs

+10-2
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,17 @@ regToSetup bak p eval shp rv = go shp rv
694694
go shp rv
695695
MirVector_Array _ -> error $ "regToSetup: MirVector_Array NYI"
696696
return $ MS.SetupArray elemTy svs
697-
go (StructShape _ _ flds) (AnyValue tpr rvs)
697+
go (StructShape tyAdt _ flds) (AnyValue tpr rvs)
698698
| Just Refl <- testEquality tpr shpTpr =
699-
MS.SetupStruct () <$> goFields flds rvs
699+
case tyAdt of
700+
M.TyAdt adtName _ _ -> do
701+
mbAdt <- use $ msbCollection . M.adts . at adtName
702+
case mbAdt of
703+
Just adt -> MS.SetupStruct adt <$> goFields flds rvs
704+
Nothing -> error $ "regToSetup: Could not find ADT named: "
705+
++ show adtName
706+
_ -> error $ "regToSetup: Found non-ADT type for struct: "
707+
++ show (PP.pretty tyAdt)
700708
| otherwise = error $ "regToSetup: type error: expected " ++ show shpTpr ++
701709
", but got Any wrapping " ++ show tpr
702710
where shpTpr = StructRepr $ fmapFC fieldShapeType flds

crucible-mir-comp/src/Mir/Compositional/Override.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ matchArg sym sc eval allocSpecs md shp rv sv = go shp rv sv
397397
rv <- liftIO $ readMaybeType sym "vector element" (shapeType shp) p
398398
go shp rv sv
399399
MirVector_Array _ -> error $ "matchArg: MirVector_Array NYI"
400-
go (StructShape _ _ flds) (AnyValue tpr rvs) (MS.SetupStruct () svs)
400+
go (StructShape _ _ flds) (AnyValue tpr rvs) (MS.SetupStruct _ svs)
401401
| Just Refl <- testEquality tpr shpTpr = goFields flds rvs svs
402402
| otherwise = error $ "matchArg: type error: expected " ++ show shpTpr ++
403403
", but got Any wrapping " ++ show tpr

doc/manual/manual.md

+74-1
Original file line numberDiff line numberDiff line change
@@ -2113,7 +2113,7 @@ implemented include the following:
21132113

21142114
* MIR specifications that use overrides (i.e., the `[MIRSpec]` argument to
21152115
`mir_verify` must always be the empty list at present)
2116-
* The ability to construct MIR `struct` or `enum` values in specifications
2116+
* The ability to construct MIR `enum` values in specifications
21172117
* The ability to specify the layout of slice values
21182118

21192119
The `String` supplied as an argument to `mir_verify` is expected to be a
@@ -2229,6 +2229,7 @@ Java types are built up using the following functions:
22292229

22302230
MIR types are built up using the following functions:
22312231

2232+
* `mir_adt : MIRAdt -> MIRType`
22322233
* `mir_array : Int -> MIRType -> MIRType`
22332234
* `mir_bool : MIRType`
22342235
* `mir_char : MIRType`
@@ -2627,9 +2628,81 @@ construct compound values:
26272628
* `mir_array_value : MIRType -> [MIRValue] -> MIRValue` constructs an array
26282629
of the given type whose elements consist of the given values. Supplying the
26292630
element type is necessary to support length-0 arrays.
2631+
* `mir_struct_value : MIRAdt -> [MIRValue] -> MIRValue` construct a struct
2632+
with the given list of values as elements. The `MIRAdt` argument determines
2633+
what struct type to create.
2634+
2635+
See the "Finding MIR alegraic data types" section for more information on how
2636+
to compute a `MIRAdt` value to pass to `mir_struct_value`.
26302637
* `mir_tuple_value : [MIRValue] -> MIRValue` construct a tuple with the given
26312638
list of values as elements.
26322639

2640+
### Finding MIR alegraic data types
2641+
2642+
We collectively refer to MIR `struct`s and `enum`s together as _algebraic data
2643+
types_, or ADTs for short. ADTs have identifiers to tell them apart, and a
2644+
single ADT declaration can give rise to multiple identifiers depending on how
2645+
the declaration is used. For example:
2646+
2647+
~~~~ .rs
2648+
pub struct S<A, B> {
2649+
pub x: A,
2650+
pub y: B,
2651+
}
2652+
2653+
pub fn f() -> S<u8, u16> {
2654+
S {
2655+
x: 1,
2656+
y: 2,
2657+
}
2658+
}
2659+
2660+
pub fn g() -> S<u32, u64> {
2661+
S {
2662+
x: 3,
2663+
y: 4,
2664+
}
2665+
}
2666+
~~~~
2667+
2668+
This program as a single `struct` declaration `S`, which is used in the
2669+
functions `f` and `g`. Note that `S`'s declaration is _polymorphic_, as it uses
2670+
type parameters, but the uses of `S` in `f` and `g` are _monomorphic_, as `S`'s
2671+
type parameters are fully instantiated. Each unique, monomorphic instantiation
2672+
of an ADT gives rise to its own identifier. In the example above, this might
2673+
mean that the following identifiers are created when this code is compiled with
2674+
`mir-json`:
2675+
2676+
* `S<u8, u16>` gives rise to `example/abcd123::S::_adt456`
2677+
* `S<u32, u64>` gives rise to `example/abcd123::S::_adt789`
2678+
2679+
The suffix `_adt<number>` is autogenerated by `mir-json` and is typically
2680+
difficult for humans to guess. For this reason, we offer a command to look up
2681+
an ADT more easily:
2682+
2683+
* `mir_find_adt : MIRModule -> String -> [MIRType] -> MIRAdt` consults the
2684+
given `MIRModule` to find an algebraic data type (`MIRAdt`). It uses the given
2685+
`String` as an identifier and the given MIRTypes as the types to instantiate
2686+
the type parameters of the ADT. If such a `MIRAdt` cannot be found in the
2687+
`MIRModule`, this will raise an error.
2688+
2689+
Note that the `String` argument to `mir_find_adt` does not need to include the
2690+
`_adt<num>` suffix, as `mir_find_adt` will discover this for you. The `String`
2691+
is expected to adhere to the identifier conventions described in the "Running a
2692+
MIR-based verification" section. For instance, the following two lines will
2693+
look up `S<u8, u16>` and `S<u32, u64>` from the example above as `MIRAdt`s:
2694+
2695+
~~~~
2696+
m <- mir_load_module "example.linked-mir.json";
2697+
2698+
s_8_16 <- mir_find_adt m "example::S" [mir_u8, mir_u16];
2699+
s_32_64 <- mir_find_adt m "example::S" [mir_u32, mir_u64];
2700+
~~~~
2701+
2702+
The `mir_adt` command (for constructing a struct type) and `mir_struct_value`
2703+
(for constructing a struct value) commands in turn take a `MIRAdt` as an
2704+
argument.
2705+
26332706
### Bitfields
26342707

26352708
SAW has experimental support for specifying `struct`s with bitfields, such as
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
all: test.linked-mir.json
2+
3+
test.linked-mir.json: test.rs
4+
saw-rustc $<
5+
$(MAKE) remove-unused-build-artifacts
6+
7+
.PHONY: remove-unused-build-artifacts
8+
remove-unused-build-artifacts:
9+
rm -f test libtest.mir libtest.rlib
10+
11+
.PHONY: clean
12+
clean: remove-unused-build-artifacts
13+
rm -f test.linked-mir.json

intTests/test_mir_verify_structs/test.linked-mir.json

+1
Large diffs are not rendered by default.
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
pub struct S1 {
2+
pub x1: u32,
3+
pub y1: u32,
4+
}
5+
6+
pub fn f1(s: S1) -> S1 {
7+
S1 {
8+
x1: s.y1.wrapping_add(1),
9+
y1: s.x1.wrapping_add(2),
10+
}
11+
}
12+
13+
pub fn g(s: &S1) -> S1 {
14+
S1 {
15+
x1: s.y1.wrapping_add(1),
16+
y1: s.x1.wrapping_add(2),
17+
}
18+
}
19+
20+
pub fn h(s: &mut S1) {
21+
let x1 = s.x1;
22+
let y1 = s.y1;
23+
s.x1 = y1.wrapping_add(1);
24+
s.y1 = x1.wrapping_add(2);
25+
}
26+
27+
// Polymorphism
28+
29+
pub struct S2<A, B> {
30+
pub x2: A,
31+
pub y2: B,
32+
}
33+
34+
pub fn f2(s: S2<u32, u32>) -> S2<u32, u32> {
35+
S2 {
36+
x2: s.y2.wrapping_add(1),
37+
y2: s.x2.wrapping_add(2),
38+
}
39+
}
40+
41+
pub struct S3(u32, u32);
42+
43+
pub fn f3(s: S3) -> S3 {
44+
match s {
45+
S3(x3, y3) => S3(y3.wrapping_add(1), x3.wrapping_add(2)),
46+
}
47+
}
48+
49+
#[repr(transparent)]
50+
pub struct S4(u32);
51+
52+
pub fn f4(s: S4) -> S4 {
53+
match s {
54+
S4(x4) => S4(x4.wrapping_add(2)),
55+
}
56+
}
57+
58+
pub struct Foo<A>(A);
59+
60+
pub fn bar<'a>(f1: &'a mut Foo<[u8; 4]>, f2: &'a mut Foo<S2<u32, u32>>) -> &'a mut Foo<S2<u32, u32>> {
61+
f1.0[1] = 42;
62+
f2.0.x2 = f2.0.y2;
63+
f2
64+
}
65+
66+
pub fn baz(_: Option<S2<u32, u32>>) {}
+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
enable_experimental;
2+
3+
m <- mir_load_module "test.linked-mir.json";
4+
5+
let s1_adt = mir_find_adt m "test::S1" [];
6+
let s2_adt = mir_find_adt m "test::S2" [mir_u32, mir_u32];
7+
let s3_adt = mir_find_adt m "test::S3" [];
8+
let s4_adt = mir_find_adt m "test::S4" [];
9+
10+
let foo_adt_1 = mir_find_adt m "test::Foo" [mir_array 4 mir_u8];
11+
let foo_adt_2 = mir_find_adt m "test::Foo" [mir_adt s2_adt];
12+
13+
let option_adt = mir_find_adt m "core::option::Option" [mir_adt s2_adt];
14+
15+
let f_spec adt = do {
16+
x1 <- mir_fresh_var "x1" mir_u32;
17+
y1 <- mir_fresh_var "y1" mir_u32;
18+
let s = mir_struct_value
19+
adt
20+
[ mir_term x1
21+
, mir_term y1
22+
];
23+
24+
mir_execute_func [s];
25+
26+
let s' = mir_struct_value
27+
adt
28+
[ mir_term {{ y1 + 1 }}
29+
, mir_term {{ x1 + 2 }}
30+
];
31+
mir_return s';
32+
};
33+
34+
35+
let f1_spec = f_spec s1_adt;
36+
let f2_spec = f_spec s2_adt;
37+
let f3_spec = f_spec s3_adt;
38+
39+
let f4_spec = do {
40+
x4 <- mir_fresh_var "x4" mir_u32;
41+
let s = mir_struct_value s4_adt [mir_term x4];
42+
43+
mir_execute_func [s];
44+
45+
let s' = mir_struct_value s4_adt [mir_term {{ x4 + 2 }}];
46+
mir_return s';
47+
};
48+
49+
let g_spec = do {
50+
s_ptr <- mir_alloc (mir_adt s1_adt);
51+
x1 <- mir_fresh_var "x1" mir_u32;
52+
y1 <- mir_fresh_var "y1" mir_u32;
53+
let s = mir_struct_value
54+
s1_adt
55+
[ mir_term x1
56+
, mir_term y1
57+
];
58+
mir_points_to s_ptr s;
59+
60+
mir_execute_func [s_ptr];
61+
62+
let s' = mir_struct_value
63+
s1_adt
64+
[ mir_term {{ y1 + 1 }}
65+
, mir_term {{ x1 + 2 }}
66+
];
67+
mir_return s';
68+
};
69+
70+
let h_spec = do {
71+
s_ptr <- mir_alloc_mut (mir_adt s1_adt);
72+
x1 <- mir_fresh_var "x1" mir_u32;
73+
y1 <- mir_fresh_var "y1" mir_u32;
74+
let s = mir_struct_value
75+
s1_adt
76+
[ mir_term x1
77+
, mir_term y1
78+
];
79+
mir_points_to s_ptr s;
80+
81+
mir_execute_func [s_ptr];
82+
83+
let s' = mir_struct_value
84+
s1_adt
85+
[ mir_term {{ y1 + 1 }}
86+
, mir_term {{ x1 + 2 }}
87+
];
88+
mir_points_to s_ptr s';
89+
};
90+
91+
let bar_spec = do {
92+
f1_ptr <- mir_alloc_mut (mir_adt foo_adt_1);
93+
f1_arr_val0 <- mir_fresh_var "f1_arr_val0" mir_u8;
94+
f1_arr_val1 <- mir_fresh_var "f1_arr_val1" mir_u8;
95+
f1_arr_val2 <- mir_fresh_var "f1_arr_val2" mir_u8;
96+
f1_arr_val3 <- mir_fresh_var "f1_arr_val3" mir_u8;
97+
let f1_arr_val = mir_array_value
98+
mir_u8
99+
[ mir_term f1_arr_val0
100+
, mir_term f1_arr_val1
101+
, mir_term f1_arr_val2
102+
, mir_term f1_arr_val3
103+
];
104+
let f1_foo_val = mir_struct_value foo_adt_1 [f1_arr_val];
105+
mir_points_to f1_ptr f1_foo_val;
106+
107+
f2_ptr <- mir_alloc_mut (mir_adt foo_adt_2);
108+
f2_s2_val0 <- mir_fresh_var "f2_s2_val0" mir_u32;
109+
f2_s2_val1 <- mir_fresh_var "f2_s2_val1" mir_u32;
110+
let f2_s2_val = mir_struct_value
111+
s2_adt
112+
[ mir_term f2_s2_val0
113+
, mir_term f2_s2_val1
114+
];
115+
let f2_foo_val = mir_struct_value foo_adt_2 [f2_s2_val];
116+
mir_points_to f2_ptr f2_foo_val;
117+
118+
mir_execute_func [f1_ptr, f2_ptr];
119+
120+
let f1_arr_val' = mir_array_value
121+
mir_u8
122+
[ mir_term f1_arr_val0
123+
, mir_term {{ 42 : [8] }}
124+
, mir_term f1_arr_val2
125+
, mir_term f1_arr_val3
126+
];
127+
let f1_foo_val' = mir_struct_value foo_adt_1 [f1_arr_val'];
128+
mir_points_to f1_ptr f1_foo_val';
129+
130+
let f2_s2_val' = mir_struct_value
131+
s2_adt
132+
[ mir_term f2_s2_val1
133+
, mir_term f2_s2_val1
134+
];
135+
let f2_foo_val' = mir_struct_value foo_adt_2 [f2_s2_val'];
136+
mir_points_to f2_ptr f2_foo_val';
137+
138+
mir_return f2_ptr;
139+
};
140+
141+
mir_verify m "test::f1" [] false f1_spec z3;
142+
mir_verify m "test::f2" [] false f2_spec z3;
143+
mir_verify m "test::f3" [] false f3_spec z3;
144+
mir_verify m "test::f4" [] false f4_spec z3;
145+
mir_verify m "test::g" [] false g_spec z3;
146+
mir_verify m "test::h" [] false h_spec z3;
147+
148+
fails (
149+
mir_verify m "test::f1" [] false f2_spec z3
150+
);
151+
fails (
152+
mir_verify m "test::f2" [] false f1_spec z3
153+
);
154+
155+
mir_verify m "test::bar" [] false bar_spec z3;
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set -e
2+
3+
$SAW test.saw

saw-remote-api/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
* The `SAW/MIR/load module` command loads a MIR JSON file into SAW.
88
* The `SAW/MIR/verify` command performs verification of a MIR function.
9+
* The `SAW/MIR/find ADT` command looks up an algebraic data type (ADT) name in
10+
a MIR module.
911

1012
See the [remote API
1113
documentation](https://github.com/GaloisInc/saw-script/blob/master/saw-remote-api/docs/SAW.rst#sawmirload-module-command)
@@ -20,6 +22,9 @@
2022
* The old `"tuple"` `setup value` has been renamed to `"struct"`. This better
2123
reflects its intended purpose of representing struct values. There is now a
2224
new `"tuple"` `setup value` that is only used to represent MIR tuples.
25+
* The API for `"struct"` `setup value`s now has a `"MIR ADT"` field. For
26+
MIR verification, this field is required. For LLVM and JVM verification,
27+
this field must be `null`, or else an error will be raised.
2328

2429
## 1.0.0 -- 2023-06-26
2530

0 commit comments

Comments
 (0)