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
|
(****************************************************************************)
(* the diy toolsuite *)
(* *)
(* Jade Alglave, University College London, UK. *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France. *)
(* *)
(* Copyright 2013-present Institut National de Recherche en Informatique et *)
(* en Automatique 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. *)
(****************************************************************************)
module Make(O:Model.Config) (S:SemExtra.S) = struct
module E = S.E
module U = MemUtils.Make(S)
let memtag = O.variant Variant.MemTag
let morello = O.variant Variant.Morello
let do_deps = O.variant Variant.Deps
let iico_ctrl_as_dep = match S.A.arch with
| `AArch64 -> true
| _ -> false
and kvm = O.variant Variant.VMSA
let is_mem_kvm =
if kvm then E.is_mem_physical
else E.is_mem
(*******************************************)
(* Complete re-computation of dependencies *)
(*******************************************)
let seq_or_id r1 r2 = S.union (S.seq r1 r2) r2
let evt_relevant x =
E.is_mem x || E.is_commit x || E.is_barrier x
|| E.is_additional_mem x
let is_mem_load_total e =
(is_mem_kvm e && E.is_load e) || E.is_additional_mem_load e
let is_load_total e = E.is_load e || E.is_additional_mem_load e
let make_procrels_deps conc =
let iico =
E.EventRel.union
conc.S.str.E.intra_causality_data
(if iico_ctrl_as_dep then
conc.S.str.E.intra_causality_control
else
E.EventRel.empty)
and rf_regs = U.make_rf_regs conc in
let iico_regs =
E.EventRel.restrict_rel
(fun e1 e2 -> not (evt_relevant e1 || evt_relevant e2)) iico in
let dd_inside = S.tr (E.EventRel.union rf_regs iico_regs) in
let success =
if O.variant Variant.Success then
S.seq
(E.EventRel.restrict_domain
(fun e1 -> E.EventSet.mem e1 conc.S.str.E.success_ports)
dd_inside)
(E.EventRel.restrict_codomain E.is_mem iico)
else E.EventRel.empty in
let e = E.EventRel.empty in
let addr = e and data = e and ctrl = e and depend = e
and ctrlisync = e and data_commit = e in
let rf = U.make_rf conc in
{ S.addr; data; ctrl; depend; ctrlisync; data_commit;
success; rf; tst=e;},iico,dd_inside
let make_procrels_nodeps is_isync conc =
let pr0,iico,dd_inside = make_procrels_deps conc in
let is_data_port =
let data_ports = conc.S.str.E.data_ports in
fun e -> E.EventSet.mem e data_ports in
let iico_rmw =
E.EventRel.inter conc.S.atomic_load_store
conc.S.str.E.intra_causality_data in
let iico_from_mem_load = (* First step of dependencies *)
E.EventRel.restrict_domain is_mem_load_total iico in
let dd_pre =
(* Most dependencies start with a mem load, a few with a mem store + ctrl
RISCV *)
S.seq
(E.EventRel.union
(E.EventRel.restrict_domain E.is_mem_store
conc.S.str.E.intra_causality_control)
iico_from_mem_load)
dd_inside in
let data_dep =
(* Data deps are (1) dd to commits (2) data deps to stores *)
let last_data =
E.EventRel.restrict_rel
(fun e1 e2 ->
E.is_commit e2 ||
(is_mem_kvm e2 && E.is_store e2 && is_data_port e1)) iico in
S.union3
(E.EventRel.restrict_domain is_mem_load_total last_data)
(S.seq dd_pre last_data)
(iico_rmw) (* Internal data dep of RMW's *)
and addr_dep =
(* Address deps are (1) dd to loads, (2) non-data deps to stores, (3) extra
abstract "memory" events (such as lock, unlock, etc. that have an address
port *)
let last_addr =
E.EventRel.restrict_rel
(fun e1 e2 ->
(is_mem_kvm e2 &&
(E.is_load e2 ||
(E.is_store e2 && not (is_data_port e1)))) ||
E.is_additional_mem e2)
(* Patch: a better solution would be a direct iico from read address register to access *)
(if memtag || kvm || morello then E.EventRel.transitive_closure iico else iico) in
S.union
(E.EventRel.restrict_domain is_mem_load_total last_addr)
(S.seq dd_pre last_addr) in
let po = U.po_iico conc.S.str in
let ctrl_one = (* For bcc: from commit to event by po *)
S.restrict E.is_bcc evt_relevant po
and ctrl_two = (* For predicated instruction from commit to event by iico *)
S.restrict E.is_pred evt_relevant (U.iico conc.S.str)
and ctrl_three = (* For structured if from event to event by instruction control *)
S.restrict is_load_total evt_relevant conc.S.str.E.control in
let ctrl =
E.EventRel.union3 ctrl_one ctrl_two ctrl_three in
let ctrl_dep =
(* All dependencies, including to reg loads *)
let dd = S.union3 dd_pre addr_dep data_dep in
let control_from_load =
E.EventRel.restrict_domain E.is_load conc.S.str.E.control in
let ddplus =
S.seq iico_from_mem_load
(S.tr (S.union dd_inside control_from_load)) in
S.restrict
(fun e -> is_mem_load_total e || E.is_mem_store e)
evt_relevant
(S.union (S.seq dd ctrl) (seq_or_id ddplus control_from_load)) in
let po =
S.restrict evt_relevant evt_relevant po in
let data_commit =
E.EventRel.restrict_codomain E.is_commit data_dep in
let data_dep = E.EventRel.restrict_codomain E.is_mem data_dep in
let ctrlisync =
try
let r1 = S.restrict is_mem_load_total is_isync ctrl_dep
and r2 = S.restrict is_isync E.is_mem po in
S.seq r1 r2
with Misc.NoIsync -> S.E.EventRel.empty in
{ pr0 with S.addr=addr_dep; data=data_dep; ctrl=ctrl_dep; depend=dd_pre;
ctrlisync; data_commit;}
let make_procrels =
if do_deps then
fun _ conc -> let pr,_,_ = make_procrels_deps conc in pr
else
make_procrels_nodeps
let pp_procrels pp_isync pr =
let pp = ["data",pr.S.data; "addr",pr.S.addr;] in
match pp_isync with
| None -> ("ctrl",pr.S.ctrl)::pp
| Some isync ->
let ctrl = E.EventRel.diff pr.S.ctrl pr.S.ctrlisync in
(Printf.sprintf "ctrl%s" isync,pr.S.ctrlisync)::
("ctrl",ctrl)::pp
(***************************)
(* A few factorized checks *)
(***************************)
open Model
let pp test conc legend vb_pp =
let module PP = Pretty.Make(S) in
Printf.eprintf "%s\n%!" legend ;
PP.show_legend test legend conc vb_pp
let pp_failure test conc legend vb_pp =
if O.debug then pp test conc legend vb_pp
(* Through *)
let check_through =
match O.through with
| ThroughAll|ThroughInvalid -> fun _ -> true
| ThroughNone -> fun ok -> ok
(* Uniproc *)
let check_uniproc test conc rf fr co =
let rel = S.unions [fr;rf;co;conc.S.pos] in
let r = E.EventRel.is_acyclic rel in
if S.O.optace = OptAce.True then assert r ;
let r = let open Model in
match O.through with
| ThroughNone|ThroughInvalid -> r
| ThroughAll -> true in
if not r then
pp_failure
test conc
(Printf.sprintf "%s: Uniproc violation" test.Test_herd.name.Name.name)
[("co",S.rt co); ("fr",fr); ("pos",S.rt conc.S.pos)] ;
r
let check_atom test conc fr co =
let ws = U.collect_mem_stores conc.S.str in
let r =
E.EventRel.for_all
(fun (r,w) ->
assert (E.same_location r w) ;
let loc =
match E.location_of w with
| Some loc -> loc | None -> assert false in
let ws = U.LocEnv.find loc ws in
not
(List.exists
(fun w' ->
E.proc_of r <> E.proc_of w' &&
E.EventRel.mem (r,w') fr &&
E.EventRel.mem (w',w) co)
ws))
conc.S.atomic_load_store in
let r = check_through r in
if not r then
pp_failure
test conc
(Printf.sprintf "%s: Atomicity violation" test.Test_herd.name.Name.name)
["co",S.rt co; "fr",fr;"r*/w*",conc.S.atomic_load_store;];
r
end
|