aboutsummaryrefslogtreecommitdiff
path: root/src/mm-alloy.adoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-alloy.adoc')
-rw-r--r--src/mm-alloy.adoc239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/mm-alloy.adoc b/src/mm-alloy.adoc
new file mode 100644
index 0000000..1352fad
--- /dev/null
+++ b/src/mm-alloy.adoc
@@ -0,0 +1,239 @@
+[[sec:alloy]]
+== Formal Axiomatic Specification in Alloy
+
+We present a formal specification of the RVWMO memory model in Alloy
+(https://alloytools.org/). This model is available online at
+https://github.com/daniellustig/riscv-memory-model.
+
+The online material also contains some litmus tests and some examples of
+how Alloy can be used to model check some of the mappings in
+Section #sec:memory:porting[[sec:memory:porting]].
+
+` `
+
+....
+////////////////////////////////////////////////////////////////////////////////
+// =RVWMO PPO=
+
+// Preserved Program Order
+fun ppo : Event->Event {
+ // same-address ordering
+ po_loc :> Store
+ + rdw
+ + (AMO + StoreConditional) <: rfi
+
+ // explicit synchronization
+ + ppo_fence
+ + Acquire <: ^po :> MemoryEvent
+ + MemoryEvent <: ^po :> Release
+ + RCsc <: ^po :> RCsc
+ + pair
+
+ // syntactic dependencies
+ + addrdep
+ + datadep
+ + ctrldep :> Store
+
+ // pipeline dependencies
+ + (addrdep+datadep).rfi
+ + addrdep.^po :> Store
+}
+
+// the global memory order respects preserved program order
+fact { ppo in ^gmo }
+....
+
+` `
+
+....
+////////////////////////////////////////////////////////////////////////////////
+// =RVWMO axioms=
+
+// Load Value Axiom
+fun candidates[r: MemoryEvent] : set MemoryEvent {
+ (r.~^gmo & Store & same_addr[r]) // writes preceding r in gmo
+ + (r.^~po & Store & same_addr[r]) // writes preceding r in po
+}
+
+fun latest_among[s: set Event] : Event { s - s.~^gmo }
+
+pred LoadValue {
+ all w: Store | all r: Load |
+ w->r in rf <=> w = latest_among[candidates[r]]
+}
+
+// Atomicity Axiom
+pred Atomicity {
+ all r: Store.~pair | // starting from the lr,
+ no x: Store & same_addr[r] | // there is no store x to the same addr
+ x not in same_hart[r] // such that x is from a different hart,
+ and x in r.~rf.^gmo // x follows (the store r reads from) in gmo,
+ and r.pair in x.^gmo // and r follows x in gmo
+}
+
+// Progress Axiom implicit: Alloy only considers finite executions
+
+pred RISCV_mm { LoadValue and Atomicity /* and Progress */ }
+....
+
+` `
+
+....
+////////////////////////////////////////////////////////////////////////////////
+// Basic model of memory
+
+sig Hart { // hardware thread
+ start : one Event
+}
+sig Address {}
+abstract sig Event {
+ po: lone Event // program order
+}
+
+abstract sig MemoryEvent extends Event {
+ address: one Address,
+ acquireRCpc: lone MemoryEvent,
+ acquireRCsc: lone MemoryEvent,
+ releaseRCpc: lone MemoryEvent,
+ releaseRCsc: lone MemoryEvent,
+ addrdep: set MemoryEvent,
+ ctrldep: set Event,
+ datadep: set MemoryEvent,
+ gmo: set MemoryEvent, // global memory order
+ rf: set MemoryEvent
+}
+sig LoadNormal extends MemoryEvent {} // l{b|h|w|d}
+sig LoadReserve extends MemoryEvent { // lr
+ pair: lone StoreConditional
+}
+sig StoreNormal extends MemoryEvent {} // s{b|h|w|d}
+// all StoreConditionals in the model are assumed to be successful
+sig StoreConditional extends MemoryEvent {} // sc
+sig AMO extends MemoryEvent {} // amo
+sig NOP extends Event {}
+
+fun Load : Event { LoadNormal + LoadReserve + AMO }
+fun Store : Event { StoreNormal + StoreConditional + AMO }
+
+sig Fence extends Event {
+ pr: lone Fence, // opcode bit
+ pw: lone Fence, // opcode bit
+ sr: lone Fence, // opcode bit
+ sw: lone Fence // opcode bit
+}
+sig FenceTSO extends Fence {}
+
+/* Alloy encoding detail: opcode bits are either set (encoded, e.g.,
+ * as f.pr in iden) or unset (f.pr not in iden). The bits cannot be used for
+ * anything else */
+fact { pr + pw + sr + sw in iden }
+// likewise for ordering annotations
+fact { acquireRCpc + acquireRCsc + releaseRCpc + releaseRCsc in iden }
+// don't try to encode FenceTSO via pr/pw/sr/sw; just use it as-is
+fact { no FenceTSO.(pr + pw + sr + sw) }
+....
+
+` `
+
+....
+////////////////////////////////////////////////////////////////////////////////
+// =Basic model rules=
+
+// Ordering annotation groups
+fun Acquire : MemoryEvent { MemoryEvent.acquireRCpc + MemoryEvent.acquireRCsc }
+fun Release : MemoryEvent { MemoryEvent.releaseRCpc + MemoryEvent.releaseRCsc }
+fun RCpc : MemoryEvent { MemoryEvent.acquireRCpc + MemoryEvent.releaseRCpc }
+fun RCsc : MemoryEvent { MemoryEvent.acquireRCsc + MemoryEvent.releaseRCsc }
+
+// There is no such thing as store-acquire or load-release, unless it's both
+fact { Load & Release in Acquire }
+fact { Store & Acquire in Release }
+
+// FENCE PPO
+fun FencePRSR : Fence { Fence.(pr & sr) }
+fun FencePRSW : Fence { Fence.(pr & sw) }
+fun FencePWSR : Fence { Fence.(pw & sr) }
+fun FencePWSW : Fence { Fence.(pw & sw) }
+
+fun ppo_fence : MemoryEvent->MemoryEvent {
+ (Load <: ^po :> FencePRSR).(^po :> Load)
+ + (Load <: ^po :> FencePRSW).(^po :> Store)
+ + (Store <: ^po :> FencePWSR).(^po :> Load)
+ + (Store <: ^po :> FencePWSW).(^po :> Store)
+ + (Load <: ^po :> FenceTSO) .(^po :> MemoryEvent)
+ + (Store <: ^po :> FenceTSO) .(^po :> Store)
+}
+
+// auxiliary definitions
+fun po_loc : Event->Event { ^po & address.~address }
+fun same_hart[e: Event] : set Event { e + e.^~po + e.^po }
+fun same_addr[e: Event] : set Event { e.address.~address }
+
+// initial stores
+fun NonInit : set Event { Hart.start.*po }
+fun Init : set Event { Event - NonInit }
+fact { Init in StoreNormal }
+fact { Init->(MemoryEvent & NonInit) in ^gmo }
+fact { all e: NonInit | one e.*~po.~start } // each event is in exactly one hart
+fact { all a: Address | one Init & a.~address } // one init store per address
+fact { no Init <: po and no po :> Init }
+....
+
+` `
+
+....
+// po
+fact { acyclic[po] }
+
+// gmo
+fact { total[^gmo, MemoryEvent] } // gmo is a total order over all MemoryEvents
+
+//rf
+fact { rf.~rf in iden } // each read returns the value of only one write
+fact { rf in Store <: address.~address :> Load }
+fun rfi : MemoryEvent->MemoryEvent { rf & (*po + *~po) }
+
+//dep
+fact { no StoreNormal <: (addrdep + ctrldep + datadep) }
+fact { addrdep + ctrldep + datadep + pair in ^po }
+fact { datadep in datadep :> Store }
+fact { ctrldep.*po in ctrldep }
+fact { no pair & (^po :> (LoadReserve + StoreConditional)).^po }
+fact { StoreConditional in LoadReserve.pair } // assume all SCs succeed
+
+// rdw
+fun rdw : Event->Event {
+ (Load <: po_loc :> Load) // start with all same_address load-load pairs,
+ - (~rf.rf) // subtract pairs that read from the same store,
+ - (po_loc.rfi) // and subtract out "fri-rfi" patterns
+}
+
+// filter out redundant instances and/or visualizations
+fact { no gmo & gmo.gmo } // keep the visualization uncluttered
+fact { all a: Address | some a.~address }
+
+////////////////////////////////////////////////////////////////////////////////
+// =Optional: opcode encoding restrictions=
+
+// the list of blessed fences
+fact { Fence in
+ Fence.pr.sr
+ + Fence.pw.sw
+ + Fence.pr.pw.sw
+ + Fence.pr.sr.sw
+ + FenceTSO
+ + Fence.pr.pw.sr.sw
+}
+
+pred restrict_to_current_encodings {
+ no (LoadNormal + StoreNormal) & (Acquire + Release)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// =Alloy shortcuts=
+pred acyclic[rel: Event->Event] { no iden & ^rel }
+pred total[rel: Event->Event, bag: Event] {
+ all disj e, e': bag | e->e' in rel + ~rel
+ acyclic[rel]
+}
+....