Skip to content

Commit cf00471

Browse files
committed
feat(circuit): polymorphic array init binding kind (painit)
A raw polymorphic `Array.init` (e.g. from `subarray256 x i = Array256.init (fun k => x.[256*i+k])`) had no circuit binding: only the monomorphic `ainit` wrapper is bindable, since `bind op`/`ainit` fixes the element width at bind time. So a spec built on `Array.init` fell into the reduce-on-demand fallback -- full reduction per element access -- which could take minutes to translate a single postcondition. Add a `painit` bv-operator kind that binds an array `init` BY PATH while leaving the element lane-width polymorphic; the width is resolved from the concrete components when the operator is translated at a concrete element type.
1 parent fbf412f commit cf00471

9 files changed

Lines changed: 82 additions & 4 deletions

File tree

src/ecCircuits.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ open CircuitSpec
383383
let bvop_is_parametric (op : EcDecl.crb_bvoperator) : bool =
384384
match op.kind with
385385
| `ASliceGet _ | `ASliceSet _ | `Extract _ | `Insert _ | `Map _ | `Get _
386-
| `AInit _ | `Init _ ->
386+
| `AInit _ | `PAInit _ | `Init _ ->
387387
true
388388
| _ -> false
389389

src/ecDecl.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ type bv_opkind = [
450450
| `Init of binding_size (* size_out *)
451451
| `Get of binding_size (* size_in *)
452452
| `AInit of binding_size * binding_size (* arr_len + size_out *)
453+
| `PAInit of binding_size (* arr_len; element size resolved at use (polymorphic) *)
453454
| `Map of binding_size * binding_size * binding_size (* size_in + size_out + arr_size *)
454455
| `A2B of (binding_size * binding_size) * binding_size (* (arr_len, elem_sz), out_size *)
455456
| `B2A of binding_size * (binding_size * binding_size) (* size in, (arr_len, elem_sz) *)

src/ecDecl.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ type bv_opkind = [
284284
| `Init of binding_size (* size_out *)
285285
| `Get of binding_size (* size_in *)
286286
| `AInit of binding_size * binding_size (* arr_len + size_out *)
287+
| `PAInit of binding_size (* arr_len; element size resolved at use (polymorphic) *)
287288
| `Map of binding_size * binding_size * binding_size (* size_in + size_out + arr_size *)
288289
| `A2B of (binding_size * binding_size) * binding_size (* (arr_len, elem_sz), out_size *)
289290
| `B2A of binding_size * (binding_size * binding_size) (* size in, (arr_len, elem_sz) *)

src/ecLowCircuits.ml

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,40 @@ module BVOps = struct
12241224
(* Should be caught by EC typechecking + binding correctness *)
12251225
| _ -> assert false
12261226
end
1227+
| `PAInit (_, Some n) -> begin
1228+
match args with
1229+
| [`Init init_f] ->
1230+
let cs = List.init n init_f in
1231+
let cinputs = List.map (fun (c : circuit) -> c.inputs) cs in
1232+
let regs =
1233+
List.map
1234+
(fun (c : circuit) ->
1235+
match c.cval with
1236+
| {type_ = CBitstring _; reg = r} -> r
1237+
(* Should be caught by EC typechecking + binding correctness *)
1238+
| _ -> assert false)
1239+
cs
1240+
in
1241+
(* The element width is not fixed by the binding (this init is
1242+
polymorphic in the element type): read it off the components,
1243+
which are concrete at the use site. *)
1244+
let w_o =
1245+
match regs with r :: _ -> C.Reg.length r | [] -> assert false
1246+
in
1247+
if not (List.for_all (fun r -> C.Reg.length r = w_o) regs) then
1248+
assert false;
1249+
(* Inputs should be uniform across components after mapping *)
1250+
if not (List.for_all (( = ) (List.hd cinputs)) cinputs) then
1251+
assert false;
1252+
let inputs = List.hd cinputs in
1253+
{
1254+
cval =
1255+
{type_ = CArray {width = w_o; count = n}; reg = C.Reg.concat regs};
1256+
inputs;
1257+
}
1258+
(* Should be caught by EC typechecking + binding correctness *)
1259+
| _ -> assert false
1260+
end
12271261
| `Init (_, Some w) -> begin
12281262
match args with
12291263
| [`Init init_f] ->
@@ -1323,7 +1357,7 @@ module BVOps = struct
13231357
| {
13241358
kind =
13251359
( `ASliceGet _ | `ASliceSet _ | `Extract _ | `Insert _ | `Map _
1326-
| `AInit _ | `Get _ | `Init _ );
1360+
| `AInit _ | `PAInit _ | `Get _ | `Init _ );
13271361
}
13281362
| _ ->
13291363
assert false (* Should be guarded by call to op_is_bvop *)

src/ecPrinting.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3923,6 +3923,7 @@ let rec pp_theory ppe (fmt : Format.formatter) (path, cth) =
39233923
| `Init _ -> "init"
39243924
| `Get _ -> "get"
39253925
| `AInit _ -> "ainit"
3926+
| `PAInit _ -> "painit"
39263927
| `Extend (_, _, false) -> "zextend"
39273928
| `Extend (_, _, true ) -> "sextend"
39283929
| `Extract _ -> "extract"

src/ecScope.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2961,6 +2961,11 @@ module Circuit = struct
29612961

29622962
| "ainit" -> (fun sz -> `AInit (as_seq2 (sz |> List.rev))), [`BV None; `A], "AInit"
29632963

2964+
(* Polymorphic array [init]: only the array is bound; the element
2965+
width is left free and resolved when translated at a concrete
2966+
element type. *)
2967+
| "painit" -> (fun sz -> `PAInit (as_seq1 sz)), [`A], "PAInit"
2968+
29642969
| "shls" ->
29652970
let mk sz = let sz1, sz2 = as_seq2 sz in `Shls (sz1, sz2) in
29662971
mk, [`BV None; `BV None], "SHLS"

src/ecSubst.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,8 @@ let subst_bv_opkind ?(red: (form -> int option) option) (s: subst) (opk: bv_opki
10541054
| `And s -> `And (ssize s)
10551055
| `Extract (s1, s2, aligned) -> `Extract (ssize s1, ssize s2, aligned)
10561056
| `Map (s1, s2, s3) -> `Map (ssize s1, ssize s2, ssize s3)
1057-
| `AInit (s1, s2) -> `AInit (ssize s1, ssize s2)
1057+
| `AInit (s1, s2) -> `AInit (ssize s1, ssize s2)
1058+
| `PAInit s -> `PAInit (ssize s)
10581059
| `Sub s -> `Sub (ssize s)
10591060
| `Get s -> `Get (ssize s)
10601061
| `Ror s -> `Ror (ssize s)

tests/circuits/bind_painit.ec

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(* Polymorphic array [init] binding ("painit"). *)
2+
3+
require import AllCore List QFABV.
4+
5+
theory Array8.
6+
type 'a t.
7+
8+
op tolist : 'a t -> 'a list.
9+
op oflist : 'a -> 'a list -> 'a t.
10+
op "_.[_]" : 'a t -> int -> 'a.
11+
op "_.[_<-_]" : 'a t -> int -> 'a -> 'a t.
12+
op init : (int -> 'a) -> 'a t.
13+
14+
end Array8.
15+
16+
bind array Array8."_.[_]" Array8."_.[_<-_]" Array8.tolist Array8.oflist Array8.t 8.
17+
realize gt0_size by auto.
18+
realize tolistP by admit.
19+
realize oflistP by admit.
20+
realize eqP by admit.
21+
realize get_setP by admit.
22+
realize get_out by admit.
23+
24+
bind op [Array8.t] Array8.init "painit".
25+
realize bvpainitP by admit.

theories/datatypes/QFABV.ec

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,10 +516,20 @@ theory BVOperators.
516516
517517
op bvainit : (int -> BV.bv) -> BV.bv A.t.
518518
519-
axiom bvainitP (f : int -> BV.bv) :
519+
axiom bvainitP (f : int -> BV.bv) :
520520
A.to_list (bvainit f) = List.mkseq (fun i => (f i)) A.size.
521521
end BVAInit.
522522
523+
(* ------------------------------------------------------------------ *)
524+
abstract theory BVPAInit.
525+
clone A.
526+
527+
op bvpainit ['a] : (int -> 'a) -> 'a A.t.
528+
529+
axiom bvpainitP ['a] (f : int -> 'a) :
530+
A.to_list (bvpainit f) = List.mkseq (fun i => (f i)) A.size.
531+
end BVPAInit.
532+
523533
(* ------------------------------------------------------------------ *)
524534
abstract theory BVMap.
525535
clone BV as BV1.

0 commit comments

Comments
 (0)