File: dumpAll.ml

package info (click to toggle)
herdtools7 7.58-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,732 kB
  • sloc: ml: 128,583; ansic: 3,827; makefile: 670; python: 407; sh: 212; awk: 14
file content (459 lines) | stat: -rw-r--r-- 15,537 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
(****************************************************************************)
(*                           the diy toolsuite                              *)
(*                                                                          *)
(* Jade Alglave, University College London, UK.                             *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France.                          *)
(*                                                                          *)
(* Copyright 2010-present Institut National de Recherche en Informatique et *)
(* en Automatique, ARM Ltd and the authors. All rights reserved.            *)
(*                                                                          *)
(* This software is governed by the CeCILL-B license under French law and   *)
(* abiding by the rules of distribution of free software. You can use,      *)
(* modify and/ or redistribute the software under the terms of the CeCILL-B *)
(* license as circulated by CEA, CNRS and INRIA at the following URL        *)
(* "http://www.cecill.info". We also give a copy in LICENSE.txt.            *)
(****************************************************************************)

open Printf

module type Config = sig
  include Top_gen.Config
  val family : string option
  val canonical_only : bool
  val fmt : int
  val no : string list
  val tarfile : string option
  val sufname : string option
  val addnum : bool
  val numeric : bool
  val lowercase : bool
  val overload : int option
  val cpp : bool
  val scope : Scope.t
  val info : MiscParser.info
  val stdout: bool
end

module Make(Config:Config)(T:Builder.S)

    = struct


      module Tar =
        Tar.Make
          (struct
            let verbose = Config.verbose
           let outname = Config.tarfile
          end)

      type edge = T.edge


(* Families *)

(* Environment for test names *)
      module Env = MyMap.Make(String)

(****************************)
(* High-level normalisation *)
(****************************)

      let normalise =
        if Config.canonical_only then
          let module Normer = Normaliser.Make(Config)(T.E) in
          fun cy ->
            let ncy = Normer.normalise (T.E.resolve_edges cy) in
            if Config.verbose > 0 then
              eprintf "Changed %s -> %s\n"
                (T.E.pp_edges cy)
                (T.E.pp_edges ncy) ;
            ncy
        else fun cy -> cy

      let mk_base = match Config.family with
      | Some b -> fun _cy -> b
      | None ->
          let module Normer = Normaliser.Make(Config)(T.E) in
          Normer.family

(* Complete name *)

      let mk_fmt base n = sprintf "%s%0*i" base Config.fmt n

      let add_suffix = match  Config.sufname with
      | None -> fun n -> n
      | Some s -> fun n -> n ^ s

      let global_mk_name =
        if Config.numeric then
          fun env base _es ->
            let n = try Env.find base env with Not_found -> 0 in
            add_suffix (mk_fmt base n),Env.add base (n+1) env
        else
          let module Namer = Namer.Make(T.A)(T.E) in
          fun env base es -> add_suffix (Namer.mk_name base es),env

      exception DupName of string

(* No need to add disambiguating numbers to numeric names *)
      let addnum =  Config.addnum

      let dup_name =
        if addnum then
          fun env name ->
            let n = try Env.find name env with Not_found -> 0 in
            (match n with | 0 -> name | _ ->  mk_fmt name n),
            Env.add name (n+1) env
        else
          fun env name ->
            try
              let _ =  Env.find name env in
              raise (DupName name)
            with Not_found -> name,Env.add name 0 env


(**********************************)
(* Computation of cycle signature *)
(**********************************)

(*
  A bit contrieved...
  - change list to array,
  - find starting index of minimal sequence in array,
  - build the signature starting from this index.
 *)

(* Signatures as strings, for the sake of compacity *)
      module W = Warn.Make(Config)

      type sigs =
          { sig_next : int ; sig_map : int T.E.Map.t ; sig_set : StringSet.t}

      let get_sig sigs e =
        try T.E.Map.find e sigs.sig_map,sigs
        with Not_found ->
          let i = sigs.sig_next in
          W.warn "New sig: %s -> %i" (T.E.pp_edge e) i ;
          if i >  0xffff then
            Warn.warn_always
              "Signatures for are more than 2 bytes, expect duplicates" ;
          i,
          { sigs with
            sig_next=i+1; sig_map = T.E.Map.add e i sigs.sig_map; }

      let sig_of sigs out e =
        let i,sigs = get_sig sigs e in
        let c1 = i land 0xff in
        let c2 = (i lsr 8) land 0xff in
        out (Char.chr c1) ;
        out (Char.chr c2) ;
        sigs

      let sig_of_shift =
        let buff = Buffer.create 16 in
        let add c = Buffer.add_char buff c in

        fun sigs t k ->
          let sz = Array.length t in
          assert(sz > 0) ;
          let incr i = if i+1 >= sz then 0 else i+1 in
          let rec do_rec sigs i =
            let sigs = sig_of sigs add t.(i) in
            let j = incr i in
            if j=k then sigs else do_rec sigs j in
          let sigs = do_rec sigs k in
          let r = Buffer.contents buff in
          Buffer.clear buff ;
          r,sigs

      let cycle_of_shift t k =
        let sz = Array.length t in
        assert(sz > 0) ;
        let incr i = if i+1 >= sz then 0 else i+1 in
        let rec do_rec i =
          let es =
            let j = incr i in
            if j=k then [] else do_rec j in
          t.(i)::es in
        do_rec k

      let find_min_shift t =

        let sz = Array.length t in
        assert (sz > 0) ;
        let incr i = if i+1 >= sz then 0 else i+1 in

        let rec c_rec k i1 i2 =
          if k <= 0 then 0
          else
            let c = T.E.compare t.(i1) t.(i2) in
            if c=0 then c_rec (k-1) (incr i1) (incr i2)
            else c in

        let rec find_rec k_min k =
          if k >= sz then k_min
          else if c_rec sz k k_min < 0 then find_rec k (k+1)
          else find_rec k_min (k+1) in

        find_rec 0 1

      let comp_sig sigs es = match es with
      | [] -> "",es,sigs
      | _::_ ->
          let t = Array.of_list es in
          let k = find_min_shift t in
          let s,sigs = sig_of_shift sigs t k in
          s, cycle_of_shift t k,sigs

      let have_seen sigs es =
        let xxx,es,sigs = comp_sig sigs es in
        if StringSet.mem xxx sigs.sig_set then
          true,es,sigs
        else
          false,es,{ sigs with sig_set = StringSet.add xxx sigs.sig_set; }

(******************)
(* Internal state *)
(******************)

      type t =
          {
           ntests : int ;        (* number of tests outputed so far *)
           sigs : sigs ;     (* Signatures of compiled tests *)
           env : int Env.t ;     (* State for getting numeric names *)
           dup : int Env.t ;     (* State for getting fresh names *)
           relaxed : T.R.SetSet.t ;
         }

      type check = edge list list -> bool
      type info = (string * string) list
      type mk_info = edge list -> info * T.R.Set.t
      let no_info _ = [],T.R.Set.empty

      type mk_name =  edge list -> string option
      let no_name _ = None

      type mk_scope =  edge list -> BellInfo.scopes option
      let no_scope _ = None

      type generator =
          (edge list -> mk_info -> mk_name -> mk_scope -> t -> t) -> t -> t

      let empty_sig =
        { sig_next = 0 ; sig_map = T.E.Map.empty ; sig_set = StringSet.empty }

      let sigs_init cys =
        let cys = List.rev_map T.E.parse_edges cys in
        List.fold_left
          (fun k es ->
            let xxx,_,k = comp_sig k es in
            { k with sig_set = StringSet.add xxx k.sig_set;} )
          empty_sig cys

      let empty_t =
        { ntests = 0 ;
          sigs = sigs_init Config.no ;
          env = Env.empty; dup = Env.empty;
          relaxed = T.R.SetSet.empty; }

(************************************** ****)
(* Check duplicates, compile and dump test *)
(*******************************************)


(* Adapt actual filename *)
      let tar_output_protect f name =
        Misc.output_protect f (Tar.outname name)

(* Compile & dump proper *)

(* Test specification *)
      type cycle =
          {
           orig : T.E.edge list ;
(* As given, for actually building the test & name. *)
           norm : T.E.edge list;
(* Normalized, for the cycle and info field. *)
         }

(* Output test proper *)
      let do_dump_test all_chan t res =
        let n = T.get_name t in
        let src =
          sprintf "%s.%s" n (if Config.cpp then "c" else "litmus") in
        if Config.stdout then
          T.dump_test_channel stdout t
        else
          tar_output_protect
            (fun chan -> T.dump_test_channel chan t)
            src ;
(* And litmus file name in @all file *)
        if not Config.stdout then
          fprintf all_chan "%s\n" src ;
        if Config.verbose > 0 then eprintf "Test: %s\n" n ;
(*    printf "%s: %s\n" n (pp_edges cycle.orig) ; *)
        { res with ntests = res.ntests+1; }

(* Dump from cycle, with specified scope tree *)
      let dump_test_st keep_name
          all_chan check init cycle info relaxed env n c mk_st res =
        (* Build test (we need number of procs...) *)
        let t = T.test_of_cycle n ~info:info ~check:check ~init cycle.orig c in
        let st = mk_st (T.get_nprocs t) in
        let n =
          if keep_name then n
          else n ^ "+" ^ Namer.of_scope st in
        let n,dup = dup_name res.dup n in
        let t = T.set_name t n in
        let t = T.set_scope t st in
        let res =
          { res with
            env; dup; relaxed= T.R.SetSet.add relaxed res.relaxed; } in
        do_dump_test all_chan t res

      let dump_test all_chan check init cycle mk_info mk_name mk_scope c res =
        let n,env = match mk_name cycle.orig with
        | None ->
            let fam = mk_base cycle.orig in
            let n,env = global_mk_name res.env fam cycle.orig in
            n,env
        | Some n ->
            n,res.env in
        let cy = T.E.pp_edges cycle.norm in
        let info,relaxed = mk_info cycle.norm in
        let info = Config.info@("Cycle",cy)::info in

        match Config.scope with
        | Scope.No ->
            let n,dup = dup_name res.dup n in
            let t = T.test_of_cycle n ~info:info ~check:check ~init cycle.orig c in
            let res =
              { res with
                env; dup; relaxed= T.R.SetSet.add relaxed res.relaxed; } in
            do_dump_test all_chan t res
        | Scope.Default ->
            let keep_name,mk_st =
              (match mk_scope cycle.orig with
              | None -> false,T.A.ScopeGen.default
              | Some st -> true,(fun _ -> st)) in
            dump_test_st
              keep_name all_chan check init
              cycle info relaxed env n c mk_st res
        | Scope.One st ->
            dump_test_st false all_chan check init cycle info relaxed env n c
              (fun _ -> st) res
        | Scope.Gen scs ->
            let t =
              T.test_of_cycle n ~info:info ~check:check ~init cycle.orig c in
            let res =
              { res with
                env; relaxed= T.R.SetSet.add relaxed res.relaxed; } in
            T.A.ScopeGen.gen scs (T.get_nprocs t)
              (fun st res ->
                let n = n ^ "+" ^ Namer.of_scope st in
                let n,dup = dup_name res.dup n in
                let t = T.set_name t n in
                let t = T.set_scope t st in
                let res = { res with dup;} in
                do_dump_test all_chan t res)
              res
        | Scope.All ->
            let t =
              T.test_of_cycle n ~info:info ~check:check ~init cycle.orig c in
            let res =
              { res with
                env; relaxed= T.R.SetSet.add relaxed res.relaxed; } in
            T.A.ScopeGen.all (T.get_nprocs t)
              (fun st res ->
                let n = n ^ "+" ^ Namer.of_scope st in
                let n,dup = dup_name res.dup n in
                let t = T.set_name t n in
                let t = T.set_scope t st in
                let res = { res with dup;} in
                do_dump_test all_chan t res)
              res
(* Compose duplicate checker and dumper *)
      let check_dump =
        if Config.canonical_only then
          fun all_chan check es mk_info mk_name mk_scope r  ->
            let es,c = T.C.resolve_edges es in
            let seen,nes,sigs = have_seen r.sigs es in
            if seen then Warn.fatal "Duplicate" ;
            let init = T.C.finish c in
            dump_test all_chan check init { orig = es ; norm = nes }
              mk_info mk_name mk_scope c { r with sigs = sigs; }
        else
          fun all_chan check es mk_info mk_name mk_scope r ->
            let es,c = T.C.resolve_edges es in
            let init = T.C.finish c in
            dump_test all_chan check init { orig = es ; norm = es ; }
              mk_info mk_name mk_scope c r

      let check_dump all_chan check es mk_info mk_name mk_scope res =
        if Config.verbose > 0 then begin
          eprintf "------------------------------------------------------\n" ;
          eprintf "Cycle: %s\n" (T.E.pp_edges es) ;
          let info,_ = mk_info es in
          List.iter
            (fun (tag,i) -> eprintf "%s: %s\n" tag i) info
        end ;
        try
          check_dump all_chan check es mk_info mk_name mk_scope res
        with
        | Misc.Fatal msg ->
            if Config.verbose > 0 then begin
              eprintf "Compilation failed: %s\n" msg
            end ;
            res
        | DupName name ->
            Warn.fatal
              "Duplicate name %s"
              name

      let check_dump all_chan check es mk_info mk_name mk_scope res =
        try
          let es =
            try normalise es
            with Normaliser.CannotNormalise msg ->
              Warn.fatal "Cannot normalise '%s'" msg in
          T.E.varatom
            es
            (fun es res ->
              if Config.debug.Debug_gen.generator then
                eprintf "Atomic variation: %s\n" (T.E.pp_edges es) ;
              check_dump all_chan check es mk_info mk_name mk_scope res)
            res
        with
        | Misc.Fatal msg|Misc.UserError msg ->
          if Config.verbose > 0 then
            eprintf "Fatal ignored: %s\n" msg ;
          res
        |Misc.Exit ->
            res
(* Exported *)
      let all ?(check=(fun _ -> true)) gen =
        let output f =
          if Config.stdout then
            f stderr
          else
            tar_output_protect f "@all"
        and print = if Config.stdout then eprintf else printf in
        output (fun all_chan ->
            fprintf all_chan
              "# %s\n" (String.concat " " (Array.to_list Sys.argv)) ;
            fprintf all_chan "# Version %s, Revision: %s\n"
              Version.version Version.rev ;
            let res =  gen (check_dump all_chan check) empty_t in
            flush stderr ;
            print
              "Generator produced %d tests\n%!"
              res.ntests ;
            if
              T.R.SetSet.exists (fun r -> not (T.R.Set.is_empty r)) res.relaxed
            then
              print "Relaxations tested: %a\n"
                T.R.pp_set_set res.relaxed) ;
        Tar.tar () ;
        Hint.close_out Config.hout

    end